package utils

import (
	"encoding/base64"
	"fmt"
	uuid "github.com/satori/go.uuid"
	"golang.org/x/crypto/scrypt"
	"reflect"
	"strings"
)

func IF(b bool, v1, v2 interface{}) interface{} {
	if b {
		return v1
	}
	return v2
}

func String(v interface{}) string {
	if v == nil {
		return ""
	}
	str := fmt.Sprintf("%v", v)
	// 去除空格
	str = strings.Trim(str, " ")
	// 去除换行符
	str = strings.Replace(str, "\n", "", -1)
	return str
}

// 加密
func ScryptPwd(pwd string) (string, error) {
	const KeyLen = 10
	//salt := make([]byte, 0, 8)
	salt := []byte{12, 2, 3, 45, 6, 32, 6, 51}
	hashPw, err := scrypt.Key([]byte(pwd), salt, 16384, 8, 1, KeyLen)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(hashPw), nil
}

// ToMap2 将结构体转为单层map
func ToMap2(in interface{}, tag string) (map[string]interface{}, error) {

	// 当前函数只接收struct类型
	v := reflect.ValueOf(in)
	if v.Kind() == reflect.Ptr { // 结构体指针
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
	}

	out := make(map[string]interface{})
	queue := make([]interface{}, 0, 1)
	queue = append(queue, in)

	for len(queue) > 0 {
		v := reflect.ValueOf(queue[0])
		if v.Kind() == reflect.Ptr { // 结构体指针
			v = v.Elem()
		}
		queue = queue[1:]
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			vi := v.Field(i)
			if vi.Kind() == reflect.Ptr { // 内嵌指针
				vi = vi.Elem()
				if vi.Kind() == reflect.Struct { // 结构体
					queue = append(queue, vi.Interface())
				} else {
					ti := t.Field(i)
					if tagValue := ti.Tag.Get(tag); tagValue != "" {
						// 存入map
						out[tagValue] = vi.Interface()
					}
				}
				break
			}
			if vi.Kind() == reflect.Struct { // 内嵌结构体
				queue = append(queue, vi.Interface())
				break
			}
			// 一般字段
			ti := t.Field(i)
			if tagValue := ti.Tag.Get(tag); tagValue != "" {
				// 存入map
				out[tagValue] = vi.Interface()
			}
		}
	}
	return out, nil
}

// 结构体转map
func S2M(v interface{}, tag string) (map[string]interface{}, error) {
	m := make(map[string]interface{})
	val := reflect.ValueOf(v)
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}
	typ := reflect.TypeOf(v)
	for i := 0; i < val.NumField(); i++ {
		FieldType := typ.Field(i)
		FieldValue := val.Field(i)
		if reflect.TypeOf(FieldType).Kind() == reflect.Struct {
			inVal := reflect.ValueOf(v)
			if inVal.Kind() == reflect.Ptr {
				inVal = inVal.Elem()
			}
			inTyp := reflect.TypeOf(v)
			for j := 0; j < inTyp.NumField(); j++ {
				inFieldType := typ.Field(i)
				inFieldValue := val.Field(i)
				m[inFieldType.Tag.Get(tag)] = inFieldValue.Interface()
			}
		} else {
			m[FieldType.Tag.Get(tag)] = FieldValue.Interface()
		}
	}
	return m, nil
}

func GetValueByFieldName(v interface{}, tag string) interface{} {
	typ := reflect.TypeOf(v).Elem()
	val := reflect.ValueOf(v).Elem()
	for i := 0; i < val.NumField(); i++ {
		cluV := val.Field(i)
		cluT := typ.Field(i)
		if cluT.Tag.Get("json") == tag {
			return cluV.Interface()
		}
	}
	return nil
}

func Uuid() string {
	return uuid.NewV4().String()
}
